<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  
</body>
<script>
  /* function isObject(value) {
    const valueType = typeof value
    return value !== null && (valueType === 'object' || valueType === 'function')
  }

  function deepClone(originValue) {
    if(!isObject(originValue)) {
      return originValue
    }
    const newObj = {}
    for(const key in originValue){
      newObj[key] = deepClone(obj[key])
    }
    return newObj
  }

  const s1 = Symbol('s1')
  const copyS1 = Symbol(s1.description)
  console.log('复制的symbol是否相等',s1 === copyS1, s1, copyS1)
  const s2 = Symbol('s2')
  // 原对象
const obj = {
  name: 'CoderBin',
  friend: {
    name: 'Jack'
  },
}
const symbolKeys = Object.getOwnPropertySymbols(obj)
console.log('symbolKeys',symbolKeys)
console.log(obj)

const cloneObj = deepClone(obj)
console.log('s1',s1,s1.description)
console.log('原对象',obj)
console.log('新拷贝对象',cloneObj, cloneObj === obj) */


const s1 = Symbol('s1')
const s2 = Symbol('s2')

const obj = {
  name: 'CoderBin',
  friend: {
    name: 'Jack'
  },
  // undefined 类型
  b: undefined,
  // 函数类型
  foo: () => { },
  // Symbol作为键值
  [s1]: 's11',
  s2: s2,
  // Set 类型
  set: new Set(['a', 'b', 'c']),
  // Map 类型
  map: new Map([
    ['a', 'aa'],
    ['b', 'bb']
  ])
}

const isObject = (originValue) => {
  return originValue !== null && (typeof originValue === 'object' || typeof originValue === 'function')
}

function deepClone(originValue, map = new Map()){
  if(originValue instanceof Set) {
    return new Set([...originValue])
  }
  if(originValue instanceof Map) {
    return new Map([...originValue])
  }
  if(typeof originValue === 'symbol') {
    return Symbol(originValue.description)
  }
  if(typeof originValue === 'function'){
    return originValue
  }


  // 判断基本类型还是引用类型
  if(!isObject(originValue)) {
    return originValue
  }

  if(map.has(originValue)){
    return map.get(originValue)
  }

  const newObj = Array.isArray(originValue) ? [] : {}
  for(const key in originValue) {
    newObj[key] = deepClone(originValue[key],map)
  }

  const symbolKeys = Object.getOwnPropertySymbols(originValue)
  console.log('symbolKeys',symbolKeys)

  for(const skey of symbolKeys) {
    console.log('skey',skey)
    newObj[skey] = deepClone(originValue[skey], map)
  }
  map.set(originValue, newObj)
  return newObj
}

const set = new Set([234,8349,])
console.log('111',deepClone(obj),obj)
/* console.log('symbols',Object.getOwnPropertySymbols(obj))
const newObj = JSON.parse(JSON.stringify(obj))
console.log('newObj',newObj)
function deepClone(obj = {}, map = new Map()) {
  if (typeof obj !== "object") {
    return obj;
  }
  if (map.has(obj)) {
    return map.get(obj);
  }\

  let result = {};
  // 初始化返回结果
  if (
    obj instanceof Array ||
    // 加 || 的原因是为了防止 Array 的 prototype 被重写，Array.isArray 也是如此
    Object.prototype.toString(obj) === "[object Array]"
  ) {
    result = [];
  }
  // 防止循环引用
  map.set(obj, result);
  for (const key in obj) {
    // 保证 key 不是原型属性
    if (obj.hasOwnProperty(key)) {
      // 递归调用
      result[key] = deepClone(obj[key], map);
    }
  }

  // 返回结果
  return result;
}
console.log('深拷贝',deepClone(obj)) */


 /* const obj = {
  name: 'CoderBin',
  friend: {
    name: 'Jack'
  }
}

// 循环引用
obj.info = obj

const newObj = JSON.parse(JSON.stringify(obj))

console.log(newObj) */
</script>
</html>
https://mp.weixin.qq.com/s/mK671ZkcOJCHdw2tsPhJ8g
声明基本变量的值，会存储在栈内存当中。
声明引用类型的值，会在堆内存中分配一块空间，来存储引用类型的值。同时在栈中也会保存一个引用地址，指向这一块空间。

声明一个变量b，并且b赋值给已存在的变量a，那么它赋值的是存储在栈中的真实值。·

而对于引用类型而言，声明一个变量o2，给变量o2赋值已存在的变量o1,那么它赋值的是栈中的引用地址。
那么很显然，改变o2或者o1,那么对于内存中存储的真正的值，那么当输出o1或者o2的时候，它们输出的结果都是会发生改变的。

浅拷贝，指的是新对象和源对象的属性会共用同一个内存空间，这里的共用指的是属性是引用类型的值会共用同一个内存空间。
简单来说，浅拷贝只会拷贝对象第一层属性，而深拷贝会递归拷贝对象所有属性，包含子对象。

浅拷贝实现方式：
1.通过Object.assign()方法，将属性复制到目标对象中，如果目标对象已有相同属性，则会覆盖原有的属性值。
2.数组的浅拷贝可以使用slice或者展开运算符（......）方法实现。

深拷贝实现方法：
1.使用JSON.parse(), JSON.stringify()方法实现。

缺陷1: 省略了undefined、函数类型的数据、symbol作为健值的数据，set、map类型的数据为空。
缺陷2: 拷贝循环引用报错。

2。使用手动递归拷贝，遍历递归的对象，对其属性判断是否是基本数据类型，是就直接拷贝，否则调用自身进行深度拷贝。
3.使用第三方库实现。